#pragma once
#include "KhaosVulkanDefine.h"

namespace Khaos
{
    class VulkanDevice;
    class VulkanCommandList;
    class VulkanPresenter;
    class VulkanSemaphore;

    class VulkanSubmitQueue : public AllocatedObject
    {
        enum SubmitType
        {
            SubmitType_CmdList,
            SubmitType_Acquire,
            SubmitType_Present,
            SubmitType_Func,
        };

        struct SubmitEntry : public AllocatedObject
        {
            SubmitType type;

            union
            {
                VulkanCommandList* cmdList;
                VulkanPresenter* presenter;
                VulkanSubmitFunc func;
            };
            
            void* userDataA = nullptr;
            uint32_t userDataB = 0;
        };

        class CmdOrderMgr
        {
        public:
            void init(VulkanSubmitQueue* submitQueue);
            void destroy();

            void submitCmd(VulkanCommandList* cmdList);
            void reset();

        private:
            VulkanSemaphore* _getNextSingalSemaphore();
            void _nextStepCmd();

        public:
            VulkanSubmitQueue* m_submitQueue = nullptr;
            std::array<VulkanSemaphore*, 1> m_cmdDepSemaphores;
            uint32_t m_cmdDepSemaphoreIndex = 0;

            std::vector<VulkanSemaphore*> m_currSingalSemaphores;
            uint32_t m_lastCmdFrameID = ~0U;
        };

    public:
        VulkanSubmitQueue(VulkanDevice* device);
        ~VulkanSubmitQueue();

        void submit(VulkanCommandList* cmdList);
        void submitCustom(VulkanSubmitFunc func, void* userData);
        void acquireNext(VulkanPresenter* presenter);
        void present(VulkanPresenter* presenter, uint32_t callFrame);

        void stop();
        void start();

        bool checkQueueThreadIdle();

    private:
        void _onThreadSubmit();
        void _onThreadFinish();

        void _submitItem(const SubmitEntry& entry);

    private:
        VulkanDevice* m_device;

        std::queue<SubmitEntry> m_submitQueue;
        std::queue<SubmitEntry> m_finishQueue;

        std::thread     m_submitThread;
        std::thread     m_finishThread;

        std::mutex              m_mutexSubmit;
        std::mutex              m_mutexFinish;
        std::condition_variable m_submitCond;
        std::condition_variable m_finishCond;
        std::atomic<bool>       m_stopped = false;

        CmdOrderMgr m_cmdOrderMgr;
    };
}

